package com.kinroy.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.kinroy.reggie.common.BaseContxt;
import com.kinroy.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@WebFilter("/*")
public class Loginfilter implements Filter {
    /*定义一个工具对象，用来判断我们的路径匹配是否是在我们的放行列表里，返回Boolean*/
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        /*将resp和req的类型强转一下，方便之后调用方法*/
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        /*获取请求的uri，注意啊注意啊，这里是uri，不是url！！！*/
        String requesturi = request.getRequestURI();
        log.info("拦截到的请求：{}", requesturi);
        /*确定放行名单*/
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/sendMsg", //放行usercontroller中的这个请求处理，不要拦截我们发送的请求
                "/user/login" //放行usercontroller中的这个请求处理，方便我们实现user登录功能
        };
        boolean check = check(requesturi, urls);

        /*当check为true时，直接放行*/
        if (check) {
            log.info("本次请求{}不需要处理", requesturi);
            filterChain.doFilter(request, response);
            return;
        }
        /*当employee访问其他页面的时候，判断是否为登录*/
        if (request.getSession().getAttribute("employee") != null) {
            /*我们现在在开发公共字段填写时出现了一个业务需求：
             * 也就是在我们实现公共自动填充的 MyMetaObjecthandler 工具类中无法获取到request对象，进而对于
             * 表属性中的ctrateuser、updateuser 就无法填写，数据库中又规定了不能为null
             * 这个时候就只能通过在一个线程 thread 中调用 threadlocal 属性来在一个线程的不同调用类中都可以获取到我们设置在
             * 其中的值，这个值只有在当前线程中的涉及到的类和操作才能访问，时有隔离的
             * */

            /*通过调用我们把threadLocal封装好的BaseContext*/
            BaseContxt.setId((Long) request.getSession().getAttribute("employee"));


            log.info("已成功登录用户id为{}", request.getSession().getAttribute("employee"));
            filterChain.doFilter(request, response);
            return;
        }

        /*当前user在移动端访问其他页码的时候，判断是否登录，登录了就放行*/
        if (request.getSession().getAttribute("user")!=null){

            log.info("已成功登录的userid为{}",request.getSession().getAttribute("user"));
            //公共字段自动填充功能
            BaseContxt.setId((Long) request.getSession().getAttribute("user"));
            filterChain.doFilter(request,response);
            return;
        }

        /*当用户没有登录，也就是说，没有执行到return，我们会联合前端的过滤器，让用户返回登录界面进行登录，
         * 只需要给前端返回一个 R数据模型的error方法即可。
         * */
        log.info("未成功登录，被拦截了！");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;

    }

    public boolean check(String requesturi, String[] urls) {
        for (String url : urls) {
            /*通过路径匹配器进行匹配，在放行名单中则返回true，不在则返回false*/
            boolean match = PATH_MATCHER.match(url, requesturi);
            if (match) {
                return true;
            }
        }
        return false;
    }
}
